package io.itit.ecp.admin.provide.renderer;

import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.Pair;
import com.alibaba.fastjson.serializer.ValueFilter;
import icu.mhb.mybatisplus.plugln.extend.Joins;
import io.itit.ecp.admin.common.constants.ParamConstants;
import io.itit.ecp.admin.common.enums.IEnergyDataEnums;
import io.itit.ecp.admin.provide.action.resp.EnergyDataResp;
import io.itit.ecp.admin.provide.action.resp.MeterDeviceResp;
import io.itit.ecp.admin.provide.filter.IEnergyDataFilter;
import io.itit.ecp.admin.provide.helper.CarbonEmissionFactorHelper;
import io.itit.ecp.admin.server.entity.EnergyDataEntity;
import io.itit.ecp.admin.server.entity.MeterDeviceEntity;
import io.itit.ecp.admin.server.entity.SceneProjectEntity;
import io.itit.ecp.admin.server.entity.SceneRoomEntity;
import io.itit.ecp.admin.server.entity.extend.EnergyDataExtEntity;
import io.itit.ecp.admin.server.service.IEnergyDataService;
import io.itit.ecp.admin.server.service.IMeterDeviceService;
import io.itit.grass.common.utils.Y666Utils;
import io.itit.grass.estelle.starter.parse.AbstractParseRenderer;
import org.apache.commons.collections.map.HashedMap;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 *
 * @Author：Roy
 * @Description：能源数据导入解析器
 * @Date：Create by 2023-12-06 17:21:33
 */
@Scope("prototype")
@Component(EnergyDataRenderer.ENTITY_NAME)
public class EnergyDataRenderer extends AbstractParseRenderer<EnergyDataExtEntity> {

    public static final String ENTITY_NAME = "energyDataRenderer";

    @Resource
    private IEnergyDataService energyDataService;

    @Resource
    private IMeterDeviceService meterDeviceService;

    private Map<String, BigDecimal> factorMap = new HashedMap();

    private Map<String, MeterDeviceResp> deviceMap;

    private Set<String> dataSet;

    private Set<String> keySet = new HashSet<>();

    @Override
    public String entityName() {
        return ENTITY_NAME;
    }

    @Override
    public int getHeadRowIndex() {
        return 1;
    }

    @Override
    public Class<EnergyDataExtEntity> getClazz() {
        return EnergyDataExtEntity.class;
    }

    @Override
    public ValueFilter getValueFilter() {
        return new IEnergyDataFilter.EnergyDataUploadFilter();
    }

    @Override
    public void processBefore() {
        deviceMap = Joins.of(MeterDeviceEntity.class)
                .leftJoin(SceneRoomEntity.class, SceneRoomEntity::getId, MeterDeviceEntity::getSceneRoomId)
                .selectAs(SceneRoomEntity::getId, MeterDeviceResp::getSceneCompanyId)
                .end()
                .joinList(MeterDeviceResp.class)
                .stream()
                .collect(Collectors.toMap(t -> t.getDeviceCode() + "::" + t.getSceneProjectId(), t -> t, (t1, t2) -> t1));
        // 大于当天的数据
        dataSet = Joins.of(EnergyDataEntity.class)
                .selectAll()
                .leftJoin(SceneProjectEntity.class, SceneProjectEntity::getId, EnergyDataEntity::getSceneProjectId)
                .select(SceneProjectEntity::getProjectName)
                .end()
                .ge(EnergyDataEntity::getSolidifyDate, getDate())
                .joinList(EnergyDataResp.class).stream().map(t -> key(t.getSolidifyDate(), t.getMeterDeviceId(), t.getSceneProjectId(), t.getEnergyTypeId())).collect(Collectors.toSet());
    }

    private String key(Date date, String deviceName, String projectName, String energyTypeName) {
        return Y666Utils.format(date, "yyyy-MM-dd HH:mm:ss") + "::" + deviceName + "::" + projectName + "::" + energyTypeName;
    }

    @Override
    public void verify(EnergyDataExtEntity entity) {
        // 只能导入当前月数据
        addErrorMsg(entity.getSolidifyDate() == null, "数据时间不能为空！");
        DateTime solidifyDate = entity.getSolidifyDate() == null ? null : DateUtil.beginOfHour(entity.getSolidifyDate());
        addErrorMsg(entity.getSolidifyDate() != null && entity.getSolidifyDate().before(getDate()), "数据时间【" + Y666Utils.format(solidifyDate, "yyyy-MM-dd HH:mm:ss") + "】已超过半年时间！");
        addErrorMsg(entity.getDeviceCode() == null, "所属设备不能为空！");
        addErrorMsg(StringUtils.isNotEmpty(entity.getProjectName()) && !deviceMap.containsKey(entity.getDeviceCode() + "::" + entity.getSceneProjectId()), "设备编码【" + entity.getDeviceCode() + "】不归属于所属项目【" + entity.getProjectName() + "】下！");
        MeterDeviceResp meterDeviceResp = deviceMap.get(entity.getDeviceCode() + "::" + entity.getSceneProjectId());
        if (meterDeviceResp != null) {
            entity.setEnergyTypeId(meterDeviceResp.getEnergyTypeId());
            entity.setSceneEdificeId(meterDeviceResp.getSceneEdificeId());
            entity.setSceneFloorId(meterDeviceResp.getSceneFloorId());
            entity.setSceneRoomId(meterDeviceResp.getSceneRoomId());
            entity.setSceneCompanyId(meterDeviceResp.getSceneCompanyId());
            entity.setMeterDeviceId(meterDeviceResp.getId());
            entity.setSceneProjectId(meterDeviceResp.getSceneProjectId());
            entity.setMeterType(meterDeviceResp.getMeterType());
            entity.setReadMeterTag(meterDeviceResp.getReadMeterTag());
            entity.setReadMeterType(meterDeviceResp.getReadMeterType());
            entity.setPurpose(meterDeviceResp.getPurpose());
        }

        addErrorMsg(entity.getReadValue() == null, "读数值不能为空！");
        String key = key(solidifyDate, entity.getMeterDeviceId(), entity.getSceneProjectId(), entity.getEnergyTypeId());

        String message = "数据时间【" + Y666Utils.format(solidifyDate, "yyyy-MM-dd HH:mm:ss")
                + "】所属设备【" + entity.getDeviceCode()
                + "】所属项目【" + entity.getProjectName();

        addErrorMsg(dataSet.contains(key), message + "】, 数据已存在！");
        addErrorMsg(keySet.contains(key), message + "】, 在导入文件中出现重复！");
        keySet.add(key);
    }

    @Override
    public int processAfter(List<EnergyDataExtEntity> resultList) {
        if (CollectionUtils.isEmpty(resultList)) {
            return 0;
        }
        List<EnergyDataEntity> list = new ArrayList<>();
        for (EnergyDataExtEntity entity : resultList) {
            entity.setSolidifyDate(DateUtil.beginOfHour(entity.getSolidifyDate()));
            entity.setReadTime(entity.getSolidifyDate());
            entity.setMeterValue(entity.getReadValue());
            entity.setReadType(IEnergyDataEnums.ReadType.readType2.readType);
            EnergyDataEntity energyDataEntity = energyDataService.lambdaQuery()
                    .lt(EnergyDataEntity::getReadTime, entity.getReadTime())
                    .eq(EnergyDataEntity::getMeterDeviceId, entity.getMeterDeviceId())
                    .orderByDesc(EnergyDataEntity::getReadTime)
                    .last(ParamConstants.LIMIT_ONE)
                    .one();
            if (energyDataEntity != null) {
                entity.setMeterValue(entity.getReadValue().subtract(energyDataEntity.getReadValue()));
            }
            //  碳排和碳减需分开
            Pair<BigDecimal, String> pair = CarbonEmissionFactorHelper.i().calc(getFactor(entity.getEnergyTypeId(), entity.getSolidifyDate()), entity.getMeterValue());
            entity.setCarbonEmission(pair.getKey());
            entity.setCarbonEmissionProcess(pair.getValue());
            setMeterDeviceId(entity);
            list.add(entity);
        }
        energyDataService.saveBatch(list, BATCH_SIZE);
        return list.size();
    }

    private BigDecimal getFactor(String type, Date date) {
        factorMap.putIfAbsent(CarbonEmissionFactorHelper.i().key(type, date), CarbonEmissionFactorHelper.i().getCacheFactor(type, date));
        return factorMap.get(CarbonEmissionFactorHelper.i().key(type, date));
    }

    private DateTime getDate() {
        return DateUtil.beginOfMonth(DateUtil.offset(DateUtil.date(), DateField.MONTH, -6));
    }

    private void setMeterDeviceId(EnergyDataExtEntity req) {
        List<MeterDeviceEntity> meterDeviceEntities = meterDeviceService.lambdaQuery()
                .eq(MeterDeviceEntity::getSceneProjectId, req.getSceneProjectId())
                .eq(MeterDeviceEntity::getDeviceCode, req.getDeviceCode())
                .orderByDesc(MeterDeviceEntity::getCreateTm)
                .list();
        if (CollectionUtils.isNotEmpty(meterDeviceEntities)) {
            req.setMeterDeviceId(meterDeviceEntities.get(0).getId());
        }
    }
}
